package com.ruoyi.business.service.impl;

import com.ruoyi.business.domain.S7VariablesConfig;
import com.ruoyi.business.service.S7VariableManagerService;
import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 基于iot-communication的S7-1500读取服务
 * 专门用于读取REAL数据类型，支持DB块地址格式如DB67.DD0
 * 
 * @author qiancheng
 * @date 2025-09-02
 */
@Service
public class BasicS7ReaderServiceImpl {

    private static final Logger log = LoggerFactory.getLogger(BasicS7ReaderServiceImpl.class);
    
    @Autowired
    private S7VariableManagerService s7VariableManagerService;
    
    private final ExecutorService executor = Executors.newFixedThreadPool(4);
    
    /**
     * 读取指定PLC的所有变量 - 使用iot-communication库
     * 
     * @param plcIp PLC IP地址
     * @return 变量名和值的映射
     */
    public Map<String, Object> readAllVariables(String plcIp) {
        log.info("开始S7-1500读取服务 - 读取PLC [{}] 的所有REAL变量", plcIp);
        
        List<S7VariablesConfig> variables = s7VariableManagerService.getVariablesByPlcIp(plcIp);
        if (variables == null || variables.isEmpty()) {
            log.warn("PLC [{}] 没有配置变量", plcIp);
            return new HashMap<>();
        }
        
        log.info("PLC [{}] 共有 {} 个变量配置", plcIp, variables.size());
        
        // 获取连接参数
        S7VariablesConfig config = variables.get(0);
        int rack = config.getRack() != null ? config.getRack().intValue() : 0;
        int slot = config.getSlot() != null ? config.getSlot().intValue() : 1;
        int port = config.getPlcPort() != null ? config.getPlcPort().intValue() : 102;
        
        // 创建结果映射
        Map<String, Object> results = new HashMap<>();
        
        // 使用iot-communication库连接S7-1500
        S7PLC s7PLC = null;
        try {
            // 创建S7PLC连接
            s7PLC = new S7PLC(EPlcType.S1500, plcIp);
            
            // 建立连接
            s7PLC.connect();
            log.info("S7-1500 PLC [{}] 连接成功 - Rack: {}, Slot: {}", plcIp, rack, slot);
            
            // 读取每个变量
            int success = 0;
            int failed = 0;
            
            for (S7VariablesConfig variable : variables) {
                try {
                    // 读取REAL类型变量
                    Float value = readRealVariable(s7PLC, variable);
                    if (value != null && variable.getVariableEnName() != null) {
                        results.put(variable.getVariableEnName(), value);
                        log.debug("变量 [{}] 读取成功, 值: {}", variable.getVariableEnName(), value);
                        success++;
                    } else {
                        log.warn("变量 [{}] 读取失败或值为null", variable.getVariableEnName());
                        failed++;
                    }
                } catch (Exception e) {
                    log.error("读取变量 [{}] 时发生异常: {}", variable.getVariableEnName(), e.getMessage());
                    failed++;
                }
            }
            
            log.info("PLC [{}] 变量读取完成: 成功 {} 个, 失败 {} 个", plcIp, success, failed);
            
        } catch (Exception e) {
            log.error("连接S7-1500 PLC [{}] 时发生异常: {}", plcIp, e.getMessage());
        } finally {
            // 关闭连接
            if (s7PLC != null) {
                try {
                    s7PLC.close();
                    log.debug("S7-1500 PLC [{}] 连接已关闭", plcIp);
                } catch (Exception e) {
                    log.warn("关闭S7-1500 PLC [{}] 连接时发生异常: {}", plcIp, e.getMessage());
                }
            }
        }
        
        return results;
    }
    
    /**
     * 读取REAL类型变量 - 使用iot-communication库
     * 
     * @param s7PLC S7PLC连接对象
     * @param variable 变量配置
     * @return REAL值
     */
    private Float readRealVariable(S7PLC s7PLC, S7VariablesConfig variable) {
        try {
            // 解析变量参数
            int dbNumber = variable.getDbNumber() != null ? variable.getDbNumber().intValue() : 67; // 默认DB67
            String startAddress = variable.getStartAddress();
            
            // 解析地址偏移量 - 支持DB67.DD0格式
            int offset = parseS7Address(startAddress);
            
            log.debug("读取REAL变量 [{}]: DB{}.DD{}", variable.getVariableEnName(), dbNumber, offset);
            
            // 使用iot-communication库读取REAL数据
            // 构造地址字符串，如"DB67.0"
            String address = "DB" + dbNumber + "." + offset;
            Float value = s7PLC.readFloat32(address);
            
            if (value != null) {
                log.debug("成功读取变量 [{}] = {}", variable.getVariableEnName(), value);
                return value;
            } else {
                log.warn("变量 [{}] 读取数据为空", variable.getVariableEnName());
                return null;
            }
            
        } catch (Exception e) {
            log.error("读取REAL变量 [{}] 异常: {}", variable.getVariableEnName(), e.getMessage());
            return null;
        }
    }
    
    /**
     * 解析S7地址格式 - 支持DB67.DD0, DB67.DD4等格式
     * 
     * @param address 地址字符串，如"DB67.DD0", "DD0", "0"等
     * @return 字节偏移量
     */
    private int parseS7Address(String address) {
        if (address == null || address.trim().isEmpty()) {
            return 0;
        }
        
        String addr = address.trim().toUpperCase();
        
        try {
            // 处理DB67.DD0格式
            if (addr.contains(".DD")) {
                String[] parts = addr.split("\\.");
                if (parts.length == 2 && parts[1].startsWith("DD")) {
                    return Integer.parseInt(parts[1].substring(2));
                }
            }
            // 处理DD0格式
            else if (addr.startsWith("DD")) {
                return Integer.parseInt(addr.substring(2));
            }
            // 处理DBD0格式
            else if (addr.startsWith("DBD")) {
                return Integer.parseInt(addr.substring(3));
            }
            // 处理纯数字格式
            else {
                return Integer.parseInt(addr);
            }
        } catch (NumberFormatException e) {
            log.error("地址解析异常: {}, 使用默认偏移量0", address);
            return 0;
        }
        
        return 0;
    }
    
    /**
     * 批量读取多个REAL变量 - 优化版本
     * 
     * @param plcIp PLC IP地址
     * @param addresses 地址列表，如["DB67.DD0", "DB67.DD4", "DB67.DD8"]
     * @return 地址和值的映射
     */
    public Map<String, Float> readMultipleRealVariables(String plcIp, List<String> addresses) {
        log.info("批量读取S7-1500 PLC [{}] 的 {} 个REAL变量", plcIp, addresses.size());
        
        Map<String, Float> results = new HashMap<>();
        
        S7PLC s7PLC = null;
        try {
            // 创建S7PLC连接 - 使用默认参数
            s7PLC = new S7PLC(EPlcType.S1500, plcIp);
            s7PLC.connect();
            log.info("S7-1500 PLC [{}] 连接成功", plcIp);
            
            // 读取每个地址
            for (String address : addresses) {
                try {
                    int offset = parseS7Address(address);
                    
                    // 读取DB67中的REAL数据
                    String s7Address = "DB67." + offset; // 默认DB67
                    Float value = s7PLC.readFloat32(s7Address);
                    
                    if (value != null) {
                        results.put(address, value);
                        log.debug("成功读取 {} = {}", address, value);
                    }
                } catch (Exception e) {
                    log.error("读取地址 {} 异常: {}", address, e.getMessage());
                }
            }
            
            log.info("批量读取完成，成功读取 {}/{} 个变量", results.size(), addresses.size());
            
        } catch (Exception e) {
            log.error("连接S7-1500 PLC [{}] 异常: {}", plcIp, e.getMessage());
        } finally {
            if (s7PLC != null) {
                try {
                    s7PLC.close();
                } catch (Exception e) {
                    log.warn("关闭连接异常: {}", e.getMessage());
                }
            }
        }
        
        return results;
    }
    
    /**
     * 异步读取指定PLC的所有变量
     * 
     * @param plcIp PLC IP地址
     * @return 异步结果
     */
    public CompletableFuture<Map<String, Object>> readAllVariablesAsync(String plcIp) {
        return CompletableFuture.supplyAsync(() -> readAllVariables(plcIp), executor);
    }
    
    /**
     * 测试连接S7-1500 PLC
     * 
     * @param plcIp PLC IP地址
     * @return 连接是否成功
     */
    public boolean testConnection(String plcIp) {
        S7PLC s7PLC = null;
        try {
            s7PLC = new S7PLC(EPlcType.S1500, plcIp);
            s7PLC.connect();
            log.info("S7-1500 PLC [{}] 连接测试成功", plcIp);
            return true;
        } catch (Exception e) {
            log.error("S7-1500 PLC [{}] 连接测试失败: {}", plcIp, e.getMessage());
            return false;
        } finally {
            if (s7PLC != null) {
                try {
                    s7PLC.close();
                } catch (Exception e) {
                    log.warn("关闭测试连接异常: {}", e.getMessage());
                }
            }
        }
    }
    
    /**
     * 读取单个REAL变量 - 简化版本
     * 
     * @param plcIp PLC IP地址
     * @param dbNumber DB块号
     * @param offset 偏移量
     * @return REAL值
     */
    public Float readSingleReal(String plcIp, int dbNumber, int offset) {
        S7PLC s7PLC = null;
        try {
            s7PLC = new S7PLC(EPlcType.S1500, plcIp);
            s7PLC.connect();
            
            // 读取DB块中的REAL数据
            String address = "DB" + dbNumber + "." + offset;
            Float value = s7PLC.readFloat32(address);
            
            if (value != null) {
                log.debug("读取DB{}.DD{} = {}", dbNumber, offset, value);
                return value;
            } else {
                log.warn("读取DB{}.DD{} 数据为空", dbNumber, offset);
                return null;
            }
            
        } catch (Exception e) {
            log.error("读取DB{}.DD{} 异常: {}", dbNumber, offset, e.getMessage());
            return null;
        } finally {
            if (s7PLC != null) {
                try {
                    s7PLC.close();
                } catch (Exception e) {
                    log.warn("关闭连接异常: {}", e.getMessage());
                }
            }
        }
    }
    
    /**
     * 资源清理
     */
    @PreDestroy
    public void destroy() {
        executor.shutdown();
        try {
            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}